using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using Magiq.Support;
using Magiq.Update;

namespace Magiq.Sql {
  public class MassUpdate<T> where T : class {
    private readonly IDependentTablesUpdateStrategy dependentTablesUpdateStrategy;
    private readonly Dictionary<string, TableUpdate<T>> tables = new Dictionary<string, TableUpdate<T>>();

    public MassUpdate(IQueryableUpdate<T> update, DataContext dataContext,
                      IDependentTablesUpdateStrategy dependentTablesUpdateStrategy) {
      this.dependentTablesUpdateStrategy = dependentTablesUpdateStrategy;
      Update = update;
      DataContext = dataContext;

      foreach (var propertySet in update.PropertySets) {
        var key = propertySet.ParentKey;

        TableUpdate<T> tableUpdate;
        if (!tables.TryGetValue(key, out tableUpdate)) {
          tableUpdate = new TableUpdate<T>(update.Where);
          tables.Add(key, tableUpdate);
        }
        tableUpdate.Set(propertySet);
      }
    }

    public IQueryableUpdate<T> Update { get; private set; }

    public int AmountOfTables {
      get { return tables.Count; }
    }

    public IEnumerable<TableUpdate<T>> Tables {
      get { return tables.Values; }
    }

    public DataContext DataContext { get; private set; }

    private int Execute(TableUpdate<T> tableUpdate) {
      var queryExecutor = new UpdateExecutor(DataContext, tableUpdate.Where, tableUpdate.PropertySets);

      var values = queryExecutor.BaseSelectedValues;
      var sets = new string[values.Length];
      var columns = queryExecutor.Columns;

      for (var i = 0; i < values.Length; i++)
        sets[i] = columns[i] + "=" + values[i];

      var updateSql = "UPDATE " + queryExecutor.BaseTableName + " SET " + sets.Join(",") + " FROM " +
                      queryExecutor.BaseWhereClause;
      var value = queryExecutor.ExecuteNonQuery(updateSql);

      return value;
    }

    public int Execute() {
      if (AmountOfTables == 1)
        return Execute(Tables.Single());

      var resultFromNotInvalidatingUpdates =
        Tables.Where(x => !x.IsWhereInvalidation).Sum(x => Execute(x));

      var invalidatingUpdates = Tables.Where(x => x.IsWhereInvalidation).ToList();

      if (invalidatingUpdates.Count == 1)
        return resultFromNotInvalidatingUpdates + Execute(invalidatingUpdates.Single());

      if (invalidatingUpdates.Count > 1)
        return resultFromNotInvalidatingUpdates +
               dependentTablesUpdateStrategy.Execute(this, invalidatingUpdates);

      return resultFromNotInvalidatingUpdates;
    }
  }
}